#ifndef DATACENTER_LOCK___H_
#define DATACENTER_LOCK___H_

#include <windows.h>   

namespace data_center {

	template<typename Tp>
	class CAutoLock
	{
	private:
		Tp	& m_Mutex;
	public:
		CAutoLock(Tp & mutex) :m_Mutex(mutex) { m_Mutex.Lock(); }
		virtual~CAutoLock() { m_Mutex.Unlock(); }
	};

	class CRWLock
	{
	public:
		enum { LOCK_LEVEL_NONE, LOCK_LEVEL_READ, LOCK_LEVEL_WRITE };
		int m_currentLevel;
		int m_readCount;
		HANDLE m_unlockEvent;
		HANDLE m_accessMutex;
		CRITICAL_SECTION m_csStateChange;
	public:
		CRWLock()
		{
			m_currentLevel = LOCK_LEVEL_NONE;
			m_readCount = 0;
			m_unlockEvent = ::CreateEvent(NULL, TRUE, FALSE, NULL);
			m_accessMutex = ::CreateMutex(NULL, FALSE, NULL);
			::InitializeCriticalSection(&m_csStateChange);
		}

		~CRWLock()
		{
			::DeleteCriticalSection(&m_csStateChange);
			if (m_accessMutex) ::CloseHandle(m_accessMutex);
			if (m_unlockEvent) ::CloseHandle(m_unlockEvent);
		}

		bool Lock(int level = LOCK_LEVEL_NONE, int timeout = INFINITE)
		{
			bool   bresult = true;
			DWORD waitResult = 0;

			waitResult = ::WaitForSingleObject(m_accessMutex, timeout);
			if (waitResult != WAIT_OBJECT_0)   return false;

			if (level == LOCK_LEVEL_READ && m_currentLevel != LOCK_LEVEL_WRITE)
			{
				::EnterCriticalSection(&m_csStateChange);
				m_currentLevel = level;
				m_readCount += 1;
				::ResetEvent(m_unlockEvent);
				::LeaveCriticalSection(&m_csStateChange);
			}
			else if (level == LOCK_LEVEL_READ &&
				m_currentLevel == LOCK_LEVEL_WRITE)
			{
				waitResult = ::WaitForSingleObject(m_unlockEvent, timeout);
				if (waitResult == WAIT_OBJECT_0)
				{
					::EnterCriticalSection(&m_csStateChange);
					m_currentLevel = level;
					m_readCount += 1;
					::ResetEvent(m_unlockEvent);
					::LeaveCriticalSection(&m_csStateChange);
				}
				else bresult = false;
			}
			else if (level == LOCK_LEVEL_WRITE &&
				m_currentLevel == LOCK_LEVEL_NONE)
			{
				::EnterCriticalSection(&m_csStateChange);
				m_currentLevel = level;
				::ResetEvent(m_unlockEvent);
				::LeaveCriticalSection(&m_csStateChange);
			}
			else if (level == LOCK_LEVEL_WRITE &&
				m_currentLevel != LOCK_LEVEL_NONE)
			{
				waitResult = ::WaitForSingleObject(m_unlockEvent, timeout);
				if (waitResult == WAIT_OBJECT_0)
				{
					::EnterCriticalSection(&m_csStateChange);
					m_currentLevel = level;
					::ResetEvent(m_unlockEvent);
					::LeaveCriticalSection(&m_csStateChange);
				}
				else bresult = false;
			}

			::ReleaseMutex(m_accessMutex);
			return bresult;

		} // lock()

		bool Unlock()
		{
			::EnterCriticalSection(&m_csStateChange);
			if (m_currentLevel == LOCK_LEVEL_READ)
			{
				m_readCount--;
				if (m_readCount == 0)
				{
					m_currentLevel = LOCK_LEVEL_NONE;
					::SetEvent(m_unlockEvent);
				}
			}
			else if (m_currentLevel == LOCK_LEVEL_WRITE)
			{
				m_currentLevel = LOCK_LEVEL_NONE;
				::SetEvent(m_unlockEvent);
			}
			::LeaveCriticalSection(&m_csStateChange);

			return true;
		}
	};

	class CAutoLockRead
	{
		CRWLock    *m_pLock;
	public:
		CAutoLockRead(CRWLock & lock) : m_pLock(&lock)
		{
			m_pLock->Lock(CRWLock::LOCK_LEVEL_READ);
		};
		~CAutoLockRead()
		{
			m_pLock->Unlock();
			m_pLock = NULL;
		};
	};

	class CAutoLockWrite
	{
		CRWLock    *m_pLock;
	public:
		CAutoLockWrite(CRWLock & lock) : m_pLock(&lock)
		{
			m_pLock->Lock(CRWLock::LOCK_LEVEL_WRITE);
		};
		~CAutoLockWrite()
		{
			m_pLock->Unlock();
			m_pLock = NULL;
		};
	};

}

#endif // DATACENTER_LOCK___H_